package dbms;

import java.util.ArrayList;

public abstract class Column<E> implements ColumnHeader, TypeHolder {

	private String name;
	private ArrayList<Cell<E>> cells;
	private boolean nullable = true, readonly = false;
	private int stringMaxLength;

	// CONSTRUCTOR

	public Column(String name) {
		this.name = name;
		cells = new ArrayList<Cell<E>>();
	}

	public Column(String name, int maxLength) {
		this.name = name;
		stringMaxLength = maxLength;
		cells = new ArrayList<Cell<E>>();
	}

	// METADATA

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@Override
	public String getType() {
		if(type() == Type.STRING) {
			return "VARCHAR(" + stringMaxLength + ")";
		}
		return Type.getTypeName(type());
	}

	public void setType(String type) {}

	public boolean isNullable() {
		return nullable;
	}

	public void setNullable(boolean isNullable) {
		nullable = isNullable;
	}

	public boolean isReadOnly() {
		return readonly;
	}

	public void setReadOnly(boolean isReadOnly) {
		readonly = isReadOnly;
	}

	public boolean isSearchable() {
		return false;
	}

	public void setSearchable(boolean isSearchable) {}

	public boolean isWritable() {
		return true;
	}

	public void setWritable(boolean isWritable) {}

	public int getStringMaxLength() {
		return stringMaxLength;
	}

	public void setStringMaxLength(int stringMaxLength) {
		this.stringMaxLength = stringMaxLength;
	}

	// DATA

	// get

	public ArrayList<Cell<E>> getCells() {
		return cells;
	}

	public Cell<E> getCell(int y) throws DBException {
		Cell<E> out = null;
		try {
			out = getCells().get(y);
		} catch (IndexOutOfBoundsException e) {
			throw new DBException(DBException.CEL_NOTFOUND);
		}
		return out;
	}

	public E getValue(int y) throws DBException {
		return getCell(y).getValue();
	}

	public int getHeight() {
		return cells.size();
	}

	// set

	public void setCell(int y, Cell<E> c) throws DBException {
		if (readonly)
			throw new DBException(DBException.CEL_WRONGINPUT);
		try {
			if (c.getValue() == null && !nullable)
				throw new DBException(DBException.CEL_WRONGINPUT);
			getCells().set(y, c);
		} catch (IndexOutOfBoundsException e) {
			throw new DBException(DBException.CEL_NOTFOUND);
		}
	}

	public void setCell(int y, final Object newValue) throws DBException {
		// read only
		if (readonly)
			throw new DBException(DBException.CEL_WRONGINPUT);
		// null value
		if (newValue == null && !nullable)
			throw new DBException(DBException.CEL_WRONGINPUT);
		// value
		try {
			setCell(y, new Cell<E>((E) newValue) {
				@Override
				public int type() {
					return Type.getTypeID(newValue.getClass().getName());
				}
			});
		} catch (Exception e) {
			throw new DBException(DBException.CEL_WRONGINPUT);
		}
	}

	// add

	public void addCell(Cell<E> c) throws DBException {
		// null cell
		if (c == null) {
			final Column<E> clmn = this;
			getCells().add(new Cell<E>((E) Type.getDefault(type())) {
				@Override
				public int type() {
					return clmn.type();
				}
			});
		}
		// null value
		if (c.getValue() == null && !nullable)
			throw new DBException(DBException.CEL_WRONGINPUT);
		// cell w\ value
		try {
			getCells().add(c);
		} catch (IndexOutOfBoundsException e) {
			throw new DBException(DBException.CEL_NOTFOUND);
		}
	}

	public void addCell(Object e) throws DBException {
		if (e instanceof String)
			e = ((String) e).subSequence(0, Math.min(((String) e).length(), stringMaxLength));
		final Object o = e;
		addCell(new Cell<E>(e) {
			@Override
			public int type() {
				return Type.getTypeID(o.getClass().getName());
			}
		});
	}

	// delete

	public void removeCell(int y) throws DBException {
		try {
			getCells().remove(y);
		} catch (IndexOutOfBoundsException e) {
			throw new DBException(DBException.CEL_NOTFOUND);
		}
	}

	public Column<E> shallowCopy() {
		final Column<E> original = this;
		Column<E> copy = new Column<E>(original.name) {
			@Override
			public int type() {
				return original.type();
			}
		};
		copy.nullable = original.nullable;
		copy.readonly = original.readonly;
		copy.stringMaxLength = original.stringMaxLength;
		copy.cells = (ArrayList<Cell<E>>) original.cells.clone();
		return copy;
	}
}
